{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0a840c76-9d70-4077-99e4-0d5366dbd8f7",
   "metadata": {},
   "source": [
    "# 安装python包folium"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "71411738-c496-4c1c-bd11-a41cbb22a191",
   "metadata": {},
   "outputs": [],
   "source": [
    "#!pip install folium #用来画世界地图\n",
    "#!pip install geopandas #用来处理和读取世界地图数据（geojson）"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b08d8639-03c0-46c0-8999-71edbba9396f",
   "metadata": {},
   "source": [
    "# 导入本脚本需要用到的python包"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "eec663ec-318f-4ebb-a7ca-9c03db5dc2ed",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import folium,branca\n",
    "from folium.features import DivIcon\n",
    "import geopandas\n",
    "import matplotlib\n",
    "import matplotlib.pylab as plt\n",
    "import numpy as np\n",
    "import branca.colormap as cm"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2709f08b-c18b-4729-b082-e911c285bb18",
   "metadata": {},
   "source": [
    "# 读取数据"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac70e308-0f23-4661-ade6-bf97f3b648b8",
   "metadata": {},
   "source": [
    "## 读取每个国家下载量数据\n",
    "这里需要注意，国家会对每个出版的地图进行审核，台湾、香港和南海诸岛必须要要属于中国，所以，需要对原始数据进行处理，将台湾、香港、澳门的数据合并到中国。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1bdf0c0-34bb-4554-b7e6-7e056ea715c2",
   "metadata": {
    "tags": []
   },
   "source": [
    "<!-- # The URL we will read our data from\n",
    "url = 'https://en.wikipedia.org/wiki/Divorce_demography'\n",
    "# read_html returns a list of tables from the URL\n",
    "tables = pd.read_html(url)\n",
    "# The data is in the second table\n",
    "table = tables[1]\n",
    "# We need to remove level 0 of columns as they are disturbing the data\n",
    "table.columns = table.columns.droplevel(0)\n",
    "# We should clean the data\n",
    "table['Country'] = table.apply(lambda row: row['Country/region'].split(' (')[0] if type(row['Country/region']) == str else row['Country/region'], axis=1)\n",
    "table -->"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "39db87f1-93d7-4bf4-b4e7-aa5e59dabe92",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['China' 'United States' 'India' 'Germany' 'United Kingdom' 'Japan'\n",
      " 'Canada' 'Australia' 'South Korea' 'Netherlands' 'Singapore' 'France'\n",
      " 'Brazil' 'Spain' 'Mexico' 'Italy' 'Denmark' 'Switzerland' 'Sweden'\n",
      " 'Pakistan' 'Russia' 'Ireland' 'Israel' 'Belgium' 'Thailand' 'Turkey'\n",
      " 'Norway' 'South Africa' 'Poland' 'Chile' 'Colombia' 'Finland' 'Malaysia'\n",
      " 'Austria' 'Philippines' 'Greece' 'Portugal' 'Indonesia' 'Iran' 'Egypt'\n",
      " 'Czech Republic' 'Argentina' 'New Zealand' 'United Arab Emirates'\n",
      " 'Ethiopia' 'Nigeria' 'Saudi Arabia' 'Kenya' 'Ukraine' 'Estonia' 'Morocco'\n",
      " 'Peru' 'Viet Nam' 'Bangladesh' 'Ecuador' 'Croatia' 'Hungary' 'Luxembourg'\n",
      " 'Romania' 'Unknown' 'Algeria' 'Ghana' 'Panama' 'Puerto Rico' 'Uruguay'\n",
      " 'Cyprus' 'Iraq' 'Serbia' 'Dominican Republic' 'Venezuela' 'Uganda'\n",
      " 'Slovakia' 'Slovenia' 'Lithuania' \"Cote d'Ivoire'\" 'Georgia' 'Kuwait'\n",
      " 'Kazakhstan' 'Zimbabwe' 'Tanzania' 'Iceland' 'Tunisia' 'Srilanka'\n",
      " 'Jordan' 'Qatar' 'Nepal' 'Benin' 'Seychelles' 'Yemen' 'North Macedonia'\n",
      " 'Lebanon' 'Paraguay' 'Sudan' 'Jamaica' 'Trinidad and Tobago' 'Costa Rica'\n",
      " 'Botswana' 'Bulgaria' 'Namibia' 'Malta' 'Oman'\n",
      " 'Palestinian Territory, Occupied' 'Senegal' 'Bolivia' 'Dem. Rep. Congo'\n",
      " 'Bahrain' 'Myanmar' 'Reunion' 'Guatemala' 'Zambia' 'Syria' 'Martinique'\n",
      " 'New Caledonia' 'Malawi' 'Latvia' 'Bosnia And Herzegovina' 'Honduras'\n",
      " 'Cameroon' 'Mongolia' 'Cuba' 'Nicaragua' 'Mauritius' 'Maldives' 'Brunei'\n",
      " 'Uzbekistan' 'Moldova' 'Armenia' 'Eritrea' 'Montenegro' 'Libya'\n",
      " 'French Guiana' 'Sierra Leone' 'Gabon' 'Liechtenstein' 'Togo' 'Belize'\n",
      " 'Belarus' 'Laos' 'Bhutan' 'Mozambique' 'Bahamas' 'Albania' 'Angola'\n",
      " 'Barbados' 'Fiji' 'Guadeloupe' 'Burkina Faso' 'Papua New Guinea'\n",
      " 'Vanuatu' 'Rwanda' 'Falkland Islands (Malvinas)' 'Madagascar' 'Andorra'\n",
      " 'Cape Verde' 'S. Sudan' 'Cambodia' 'Kyrgyzstan'\n",
      " 'EUROPE - UNKNOWN COUNTRY' 'El Salvador' 'Greenland' 'Aruba' 'Guinea']\n"
     ]
    }
   ],
   "source": [
    "df=pd.read_excel(os.path.expanduser(\"data.xlsx\"))\n",
    "#merge China main land, Hongkong, Taiwan and Macao.\n",
    "df.loc[df.CountryName=='China','Count']= df.loc[df.CountryName.isin(['China','Hong Kong','Taiwan','Macao']),'Count'].sum()\n",
    "df=df.loc[~ df.CountryName.isin(['Hong Kong','Taiwan','Macao'])]\n",
    "#为了使数据中的国家名字与后面的地图数据中的国家名字匹配，需要将部分国家的名字替换为地图数据中的名字\n",
    "df.CountryName.replace({'Iran, Islamic Republic of...':'Iran',\n",
    "                       'Russian Federation':'Russia','Sri Lanka':'Srilanka',\n",
    "                       'Venezuela, Bolivarian Republic of...':'Venezuela',\n",
    "                       'The former Yugoslav Republic of Macedonia':'North Macedonia','Syrian Arab Republic':'Syria',\n",
    "                        'Brunei Darussalam':'Brunei','Republic of Moldova':'Moldova',\n",
    "                        'Congo, Democratic Republic':'Dem. Rep. Congo',\"Lao People's Democratic Republic\":'Laos',\n",
    "                        'Libyan Arab Jamahiriya':'Libya','South Sudan':'S. Sudan',\n",
    "                       'United Republic of Tanzania':'Tanzania',\n",
    "                        'Bosnia and Herzegovina':'Bosnia And Herzegovina',\"Korea,Democratic People'S Republic Of\":'North Korea',\n",
    "                       },\n",
    "                       inplace=True)\n",
    "print(df.CountryName.unique())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be8fadd1-31af-4960-9751-208bbd690ecc",
   "metadata": {},
   "source": [
    "## 读取世界地图数据\n",
    "这里，使用geopandas读取中科院全球国家行政边界数据,<br>\n",
    "地图数据下载网址：中国科学院地理科学与资源研究所，<br>\n",
    "链接https://www.resdc.cn/Default.aspx，选择【全球100万基础地理数据】分类下面的【全球国家行政边界数据】下载解压，也可以直接从前面分享的github链接获取地图数据。\n",
    "<br><br>\n",
    "需要注意的是，从其它地方下载的世界地图（大多来自西方国家），西藏的边界线是有问题的，跟国家官方给的边界线不一致，如果用那些数据来画图，审核肯定无法通过。还有台湾、钓鱼岛和南海诸岛，也存在问题。所以，我们最后使用的是中科院提供的世界地图数据来画图。<br><br>\n",
    "如果需要画中国地图，比如不同省、市的分布，可以将这里的世界地图数据替换为中国地图即可（一般是geojson格式的地图数据，也可以在上面中科院官网下载中国地图数据）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "9e5d55dd-70c1-4d65-906d-55d93d92f5e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[nan 'Greenland' 'Canada' 'United States' 'Saint Pierre And Miquelon'\n",
      " 'Bermuda' 'Bahamas' 'Turks And Caicos Islands' 'Cuba' 'Mexico'\n",
      " 'Cayman Islands' 'Haiti' 'Puerto Rico' 'Virgin Islands,British'\n",
      " 'Dominican Republic' 'Virgin Islands,U.S.' 'Anguilla' 'Jamaica'\n",
      " 'Saint Kitts And Nevis' 'Antigua And Barbuda' 'Montserrat' 'Belize'\n",
      " 'Guadeloupe' 'Guatemala' 'Dominica' 'Martinique' 'Nicaragua'\n",
      " 'Saint Lucia' 'El Salvador' 'Honduras' 'Barbados'\n",
      " 'Saint Vincent And The Grenadines' 'Aruba' 'Colombia' 'Grenada'\n",
      " 'Netherlands Antilles' 'Trinidad And Tobago' 'Costa Rica' 'France'\n",
      " 'Panama' 'Marshall Islands' 'Palau' 'Kiribati' 'Nauru' 'Solomon Islands'\n",
      " 'Papua New Guinea' 'Cook Islands' 'American Samoa' 'Australia' 'Vanuatu'\n",
      " 'Fiji' 'Tonga' 'Niue' 'New Caledonia' 'Pitcairn' 'Norfolk Island'\n",
      " 'Heard Island And Mcdonald Islands' 'Bouvet Island'\n",
      " 'Northern Mariana Islands' 'Antarctica'\n",
      " 'South Georgia And The South Sandwich Islands' 'Tuvalu' 'Tokelau'\n",
      " 'French Polynesia' 'French Southern Territories' 'Samoa'\n",
      " 'Micronesia,Federated States Of' 'Wallis And Futuna'\n",
      " 'Cocos(Keeling) Islands' 'Prince Edward Island' 'New Zealand'\n",
      " 'Wake Island' 'Algeria' 'Tunisia' 'Libya' 'Morocco' 'Egypt'\n",
      " 'Western Sahara' 'Mali' 'Mauritania' 'Niger' 'Sudan' 'Senegal'\n",
      " 'Cape Verde' 'Ethiopia' 'Gambia' 'Burkina Faso' 'Eritrea' 'Guinea-Bissau'\n",
      " 'Djibouti' 'Guinea' 'Chad' 'Nigeria' \"Cote D'Ivoire'\"\n",
      " 'Central African Republic' 'Ghana' 'Sierra Leone' 'Benin' 'Togo'\n",
      " 'Liberia' 'Cameroon' 'Uganda' 'Equatorial Guinea' 'Congo'\n",
      " 'Sao Tome And Principe' 'Gabon' 'Somalia' 'Rwanda' 'Kenya'\n",
      " 'Dem. Rep. Congo' 'Burundi' 'Tanzania' 'Angola' 'Seychelles' 'Malawi'\n",
      " 'Mayotte' 'Zambia' 'Madagascar' 'Mozambique' 'Mauritius' 'Zimbabwe'\n",
      " 'Reunion' 'Namibia' 'Botswana' 'Swaziland' 'Lesotho' 'South Africa'\n",
      " 'Saint Helena' 'Comoros' 'Canarias' 'Madeira' 'Venezuela' 'Guyana'\n",
      " 'Suriname' 'French Guiana' 'Ecuador' 'Brazil' 'Bolivia' 'Peru' 'Paraguay'\n",
      " 'Argentina' 'Uruguay' 'Chile' 'Falkland Islands(Malvinas)'\n",
      " 'Svalbard And Jan Mayen' 'Russia' 'Sweden' 'Iceland' 'Norway'\n",
      " 'Faroe Islands' 'Finland' 'United Kingdom' 'Estonia' 'Latvia' 'Lithuania'\n",
      " 'Denmark' 'Belarus' 'Germany' 'Netherlands' 'Poland' 'Ireland' 'Belgium'\n",
      " 'Luxembourg' 'Czech Republic' 'Slovakia' 'Moldova' 'Austria' 'Hungary'\n",
      " 'Switzerland' 'Liechtenstein' 'Slovenia' 'Romania' 'Serbia' 'Ukraine'\n",
      " 'Bosnia And Herzegovina' 'Croatia' 'San Marino' 'Monaco' 'Italy'\n",
      " 'Bulgaria' 'Andorra' 'North Macedonia' 'Montenegro' 'Vatican' 'Albania'\n",
      " 'Greece' 'Gibraltar' 'Malta' 'Portugal' 'Spain' 'Turkmenistan'\n",
      " 'Kyrgyzstan' 'Armenia' 'Syria' 'Lebanon' 'Afghanistan' 'Iraq' 'Kuwait'\n",
      " 'Nepal' 'Bhutan' 'Laos' 'Maldives' 'Brunei' 'East Timor'\n",
      " 'Christmas Island' 'Mongolia' 'Caspian Sea' 'Jordan' 'Bahrain' 'Qatar'\n",
      " 'Georgia' 'Israel' 'Cyprus' 'Iran' 'Pakistan' 'Azerbaijan' 'Yemen'\n",
      " 'Bangladesh' 'Singapore' 'Kazakhstan' 'Area Under Dispute' 'Cambodia'\n",
      " 'Srilanka' 'British Indian Ocean Territory' 'Tajikistan' 'Saudi Arabia'\n",
      " 'Myanmar' 'Japan' 'South Korea' 'United Arab Emirates' 'Malaysia' 'India'\n",
      " 'Indonesia' 'Philippines' 'Oman' 'Thailand' 'Uzbekistan' 'Turkey'\n",
      " 'North Korea' 'Viet Nam' 'China']\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>OBJECTID</th>\n",
       "      <th>NAME</th>\n",
       "      <th>FENAME</th>\n",
       "      <th>FCNAME</th>\n",
       "      <th>SOC</th>\n",
       "      <th>POP</th>\n",
       "      <th>ELEMID</th>\n",
       "      <th>SHAPE_LENG</th>\n",
       "      <th>SHAPE_AREA</th>\n",
       "      <th>ID1</th>\n",
       "      <th>CountryName</th>\n",
       "      <th>Count</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>None</td>\n",
       "      <td>None</td>\n",
       "      <td>None</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0</td>\n",
       "      <td>154.008856</td>\n",
       "      <td>27.507542</td>\n",
       "      <td>1</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>Greenland</td>\n",
       "      <td>Greenland</td>\n",
       "      <td>¸ñÁêÀ¼</td>\n",
       "      <td>GRL</td>\n",
       "      <td>6.0</td>\n",
       "      <td>1</td>\n",
       "      <td>1359.592591</td>\n",
       "      <td>662.855357</td>\n",
       "      <td>2</td>\n",
       "      <td>Greenland</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>Canada</td>\n",
       "      <td>Canada</td>\n",
       "      <td>¼ÓÄÃ´ó</td>\n",
       "      <td>CAN</td>\n",
       "      <td>3166.0</td>\n",
       "      <td>2</td>\n",
       "      <td>3635.736641</td>\n",
       "      <td>1692.808594</td>\n",
       "      <td>3</td>\n",
       "      <td>Canada</td>\n",
       "      <td>1763.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>United States</td>\n",
       "      <td>United States Of America</td>\n",
       "      <td>ÃÀ¹ú</td>\n",
       "      <td>USA</td>\n",
       "      <td>28837.0</td>\n",
       "      <td>3</td>\n",
       "      <td>1261.087368</td>\n",
       "      <td>1100.995648</td>\n",
       "      <td>4</td>\n",
       "      <td>United States</td>\n",
       "      <td>15479.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>Saint Pierre And Miquelon</td>\n",
       "      <td>Saint Pierre and Miquelon</td>\n",
       "      <td>Ê¥Æ¤°£¶ûºÍÃÜ¿ËÂ¡</td>\n",
       "      <td>SPM</td>\n",
       "      <td>1.0</td>\n",
       "      <td>4</td>\n",
       "      <td>1.725672</td>\n",
       "      <td>0.026938</td>\n",
       "      <td>5</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   OBJECTID                       NAME                     FENAME  \\\n",
       "0  1         NaN                        None                        \n",
       "1  2         Greenland                  Greenland                   \n",
       "2  3         Canada                     Canada                      \n",
       "3  4         United States              United States Of America    \n",
       "4  5         Saint Pierre And Miquelon  Saint Pierre and Miquelon   \n",
       "\n",
       "             FCNAME   SOC      POP ELEMID   SHAPE_LENG   SHAPE_AREA  ID1  \\\n",
       "0  None              None  0.0      0      154.008856   27.507542    1     \n",
       "1  ¸ñÁêÀ¼            GRL   6.0      1      1359.592591  662.855357   2     \n",
       "2  ¼ÓÄÃ´ó            CAN   3166.0   2      3635.736641  1692.808594  3     \n",
       "3  ÃÀ¹ú              USA   28837.0  3      1261.087368  1100.995648  4     \n",
       "4  Ê¥Æ¤°£¶ûºÍÃÜ¿ËÂ¡  SPM   1.0      4      1.725672     0.026938     5     \n",
       "\n",
       "     CountryName    Count  \n",
       "0  NaN           NaN       \n",
       "1  Greenland      1.0      \n",
       "2  Canada         1763.0   \n",
       "3  United States  15479.0  \n",
       "4  NaN           NaN       "
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Read the geopandas dataset\n",
    "data = geopandas.read_file(\"世界国家.shp\")\n",
    "data.crs=\"epsg:4326\"\n",
    "data.to_crs(crs=\"epsg:3857\")\n",
    "data.to_file('World.geojson', driver='GeoJSON')\n",
    "#国家名字每个单词首字母大写\n",
    "data.NAME=data.NAME.apply(lambda x:x.title() if not pd.isna(x) else np.nan)\n",
    "data.NAME.replace({'Russian Federation':'Russia','Cote D¡¯Ivoire':\"Cote D'Ivoire'\",\n",
    "                  \"Korea,Democratic People'S Republic Of\":'North Korea',\n",
    "                   'Macedonia,The Former Yugoslav Republic Of':'North Macedonia',\n",
    "                   'Syrian Arab Republic':'Syria','Korea, Republic Of':'South Korea',\n",
    "                   'Congo,The Democratic Republic Of The':'Dem. Rep. Congo',\n",
    "                   'United States of America':'United States'\n",
    "                  },inplace=True)\n",
    "#讲下载量数据中的国家名字也做同样的处理\n",
    "df.CountryName=df.CountryName.apply(lambda x:x.title() if not pd.isna(x) else np.nan)\n",
    "#把两个数据框拼接起来，拼接后的数据框，既包含世界地图坐标数据，也包含我们需要展示的数据（下载量）\n",
    "data = data.merge(df, how=\"left\", left_on=['NAME'], right_on=['CountryName'])\n",
    "print(data.NAME.unique())\n",
    "data.drop('geometry',axis=1).head() \n",
    "#geometry这一列包含每个国家的坐标和边界信息，非常庞大，所以就不展示geometry这一列了"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7b50f9c2-5fed-45db-b3b8-b20a51c3024a",
   "metadata": {},
   "source": [
    "## 将无法匹配的国家名字打印出来，如果有需要，可以向上面df.CountryName.replace中添加字典的内容来匹配"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "86a405ca-d2e8-4b8e-9fbe-81a4f426b0be",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array(['Unknown', 'Palestinian Territory, Occupied',\n",
       "       'Falkland Islands (Malvinas)', 'S. Sudan',\n",
       "       'Europe - Unknown Country'], dtype=object)"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.loc[~ df.CountryName.isin(data.NAME.tolist())].CountryName.values"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5353c70-917a-43cd-8033-625d2e944f10",
   "metadata": {},
   "source": [
    "# 画世界地图\n",
    "folium.Map的主要参数信息如下：<br>\n",
    "location： 设定地图中心点<br>\n",
    "width, height：将地图的宽度和高度进行缩放<br>\n",
    "tiles： 地图瓦片，参考https://leaflet-extras.github.io/leaflet-providers/preview/ 和 https://juejin.cn/post/7116708374279880734"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "2c4bc1fb-4094-49a3-ad1e-1c6ff422b1a7",
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 2, 8, 26, 82, 249, 755, 2283, 6894, 20813, 62829]\n"
     ]
    }
   ],
   "source": [
    "# Create a map\n",
    "my_map = folium.Map(title=\"World Map\",location=(50,5),max_zoom=1,control_scale=True,\n",
    "                    prefer_canvas=True,zoom_control=False,\n",
    "                    width='80%',height='90%',zoom_start=2.49,#tiles='Stamen Terrain', #Stamen Watercolor\n",
    "                    # titles=\"http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunityENG/MapServer/tile/{z}/{y}/{x}\",\n",
    "                   tiles=folium.TileLayer('https://{s}.tile.thunderforest.com/mobile-atlas/{z}/{x}/{y}.png?apikey={apikey}',\n",
    "                          attr='&copy; <a href=\"http://www.thunderforest.com/\">Thunderforest</a>, &copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n",
    "                          apikey='pk.eyJ1IjoiZGluZ3diIiwiYSI6ImNsY3doNmluazBmd2Qzb29lbzVrYXltdjYifQ.H8sWvLIDzRD7hbZDYlbUCQ',maxZoom=24,overlay=True)\n",
    "                   )\n",
    "\n",
    "\n",
    "\n",
    "data['Y']=data.Count.apply(lambda x:np.log2(x+1) if not pd.isna(x) else np.nan)\n",
    "max_v = data.Y.max()\n",
    "def get_color(x,name):\n",
    "    if pd.isna(name):\n",
    "        return 'darkgrey'\n",
    "    if pd.isna(x):\n",
    "        return 'darkgrey'\n",
    "    return matplotlib.colors.rgb2hex(plt.get_cmap('Spectral_r')(x/max_v))\n",
    "    # return cmap(x)\n",
    "\n",
    "ticks=[]\n",
    "for i in np.arange(start=0,stop=1.1,step=0.1):\n",
    "    ticks.append(int(2**(i * max_v) -1))\n",
    "print(ticks)\n",
    "cmap = cm.LinearColormap([plt.get_cmap('Spectral_r')(i) for i in np.arange(start=0,stop=1.1,step=0.1)],\n",
    "                         index=np.arange(start=0,stop=1.1,step=0.1),vmin=0, vmax=1,max_labels=20).to_step(10) #tick_labels=ticks,\n",
    "\n",
    "cmap.caption=\"Number of download\"\n",
    "my_map.add_child(cmap)\n",
    "\n",
    "# add different color to each country according to the number of download\n",
    "folium.GeoJson(\n",
    "    data,\n",
    "    style_function=lambda feature: {\n",
    "        'fillColor': get_color(feature['properties']['Y'],feature['properties']['NAME']),\n",
    "        'color': 'gold',\n",
    "        'fillOpacity':1,\n",
    "        'opacity':0.7,\n",
    "        'weight': 1,\n",
    "        'dashArray': '0.8, 0.8'\n",
    "    }\n",
    ").add_to(my_map)\n",
    "\n",
    "\n",
    "#add country names onto the map.\n",
    "for i,row in data.iterrows():\n",
    "    if pd.isna(row['CountryName']):\n",
    "        continue\n",
    "    center=row.geometry.centroid\n",
    "    if row.geometry.area <= 5:\n",
    "        continue\n",
    "    fontsize= min(3+row.geometry.area / 70,10)\n",
    "    folium.map.Marker(\n",
    "        [center.y, center.x],\n",
    "        icon=DivIcon(\n",
    "            html='<div style=\"color:black; font-size: %spt; font-weight: bold\"; opacity: 0.5>%s</div>'%(fontsize,row['NAME']),\n",
    "            )\n",
    "        ).add_to(my_map)\n",
    "\n",
    "my_map.save('map1.html')\n",
    "#my_map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "621fda1a-4533-450d-816e-ce3cc7353bb1",
   "metadata": {},
   "source": [
    " 这里需要注意，最难的地方在于图例，因为这个数据分布不均匀，大部分下载量都很小，集中在1-3000，最大的是中国（下载量是6万多，其次是美国，1万多，排第三的只有3000左右）。<br>\n",
    " 如果用线性的图例配色，那么，1-3000的那些绝大多数国家，颜色就都是一样的（比如都是蓝色），只有中国是红色，就不好看。<br>\n",
    " 所以，我们需要使用非线形的colormap。<br>\n",
    " 我首先对下载量data.count进行log转换，转换后，最大值是15左右，然后再缩放（除以最大值）到0-1之间，再用colormap采用线性的colormap来解决上述问题。<br>\n",
    " 此外，我们在标注国家名字时，将名字标注在每个国家的中心位置，字体大小还可以根据该国家的面积来自动缩放。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "768bf88c-bef7-4bbd-b368-7aa3d40a736c",
   "metadata": {},
   "source": [
    "## 处理图例-方法1\n",
    "因为我们目前的下载量被缩放到了0-1之间，但是地图的图例上面需要展示展示的下载量，而不是缩放之后的小数。<br>\n",
    "有2个方法可以修改图例，第一个方法是将图例上面缩放之后的小数0.1，0.2，0.3，通过逆向运算，就可以算出处理之前的真实下载量，我们先对下载量进行了log2转换，再除以最大值（max_v）:data['Y']=data.Count.apply(lambda x:np.log2(x+1) if not pd.isna(x) else np.nan)。<br>\n",
    "那么逆运算就是先乘以max_v再做2的幂指数运算，最后减去1，就是我们原始的下载量："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "3bc3b9b3-c671-4107-b778-ac1d0b334c15",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0, 2, 8, 26, 82, 249, 755, 2283, 6894, 20813, 62829]\n"
     ]
    }
   ],
   "source": [
    "R=[]\n",
    "for i in np.arange(start=0,stop=1.1,step=0.1):\n",
    "    R.append(int(2**(i * max_v) -1))\n",
    "print(R)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5f170d5-69fc-49d4-a79e-a0e40b64c9c8",
   "metadata": {},
   "source": [
    "上面打印出来的，就是图例上对应的原始下载量，然后手动将图例上面的小数替换成上述真实数字即可。手动在html或者pdf编辑器中将0，0.2，0.4，0.6，0.8，1替换成[0, 2, 8, 26, 82, 249, 755, 2283, 6894, 20813, 62829]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0baa203-1e88-4286-9cef-3a12f8ea2950",
   "metadata": {},
   "source": [
    "## 处理图例-方法2\n",
    "我们也可以用Javascript代码自动化处理，大致原理就是用javscript读取识别图例的tick labels，做逆运算，自动替换为原始下载量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "18b785fc-a140-4568-9ee1-29bedf1c24c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from branca.element import Element\n",
    "e = Element(\"\"\"\n",
    "  var ticks = document.querySelectorAll('div.legend g.tick text')\n",
    "  for(var i = 0; i < ticks.length; i++) {\n",
    "    var value = parseFloat(ticks[i].textContent.replace(',', ''))\n",
    "    console.log(value)\n",
    "    var newvalue = (Math.pow(2, value * 15.939188921332656).toFixed(0)-1).toString()\n",
    "    ticks[i].textContent = newvalue\n",
    "  }\n",
    "\"\"\")\n",
    "html = cmap.get_root()\n",
    "html.script.get_root().render()\n",
    "html.script.add_child(e)\n",
    "#如果需要保存成html可以添加\n",
    "my_map.save('map2.html')\n",
    "#my_map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0361218-480e-4c96-925a-0202f80cc1f9",
   "metadata": {},
   "source": [
    "# 画世界地图的备选方法-folium.Choropleth函数\n",
    "最后，在folium包中，还可以用另外一个函数Choropleth函数也可以画出上面的图，但是这个方法画出来的图，图例也是有问题的，需要手动修改图例。<br>\n",
    "感兴趣的朋友可以研究一下，附上代码："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "d2903703-188b-4c67-b946-b709ba17ac0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "scales=[1,5,10,50,100,200,500,1000,2000,3000,20000,62830]\n",
    "data['ID']=data.index.tolist()\n",
    "\n",
    "my_map = folium.Map(title=\"World Map\",location=(50,5),max_zoom=1,control_scale=True,\n",
    "                    prefer_canvas=True,zoom_control=False,\n",
    "                    width='80%',height='90%',zoom_start=2.49,#tiles='Stamen Terrain', #Stamen Watercolor\n",
    "                    # titles=\"http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunityENG/MapServer/tile/{z}/{y}/{x}\",\n",
    "                   tiles=folium.TileLayer('https://{s}.tile.thunderforest.com/mobile-atlas/{z}/{x}/{y}.png?apikey={apikey}',\n",
    "                          attr='&copy; <a href=\"http://www.thunderforest.com/\">Thunderforest</a>, &copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n",
    "                          apikey='pk.eyJ1IjoiZGluZ3diIiwiYSI6ImNsY3doNmluazBmd2Qzb29lbzVrYXltdjYifQ.H8sWvLIDzRD7hbZDYlbUCQ',maxZoom=24,overlay=True)\n",
    "                   )\n",
    "\n",
    "c = folium.Choropleth(\n",
    "    geo_data=data,\n",
    "    name='choropleth',\n",
    "    data=data,\n",
    "    columns=['ID', 'Count'],\n",
    "    key_on='feature.id',\n",
    "    fill_color='Spectral_r',#'red',#'RdYlGn_r',\n",
    "    nan_fill_color='darkgray',\n",
    "    fill_opacity=0.8,\n",
    "    line_opacity=0.5,\n",
    "    legend_name='No.Download',\n",
    "    highlight=True,\n",
    "    threshold_scale=scales\n",
    ")\n",
    "\n",
    "# print(c._children)\n",
    "for child in c._children:\n",
    "        if child.startswith(\"color_map\"):\n",
    "            del c._children[child]\n",
    "\n",
    "c.add_to(my_map)\n",
    "\n",
    "cmap=cm.linear.Spectral_11.to_step(11).scale(1,62830)\n",
    "\n",
    "cmap.caption=\"Number of download\"\n",
    "my_map.add_child(cmap)\n",
    "\n",
    "#add country names onto the map.\n",
    "for i,row in data.iterrows():\n",
    "    if pd.isna(row['CountryName']):\n",
    "        continue\n",
    "    center=row.geometry.centroid\n",
    "    if row.geometry.area <= 5:\n",
    "        continue\n",
    "    fontsize= min(3+row.geometry.area / 70,10)\n",
    "    folium.map.Marker(\n",
    "        [center.y, center.x],\n",
    "        icon=DivIcon(\n",
    "            html='<div style=\"color:black; font-size: %spt; font-weight: bold\"; opacity: 0.5>%s</div>'%(fontsize,row['NAME']),\n",
    "            )\n",
    "        ).add_to(my_map)\n",
    "    \n",
    "my_map.save('map3.html')\n",
    "#my_map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "978beb21-1652-476b-b16e-65893e46990e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
